#include "Sock.hpp"
#include <iostream>
#include <sys/poll.h>
#include <string>

#define NUM (64)

void usage(string s)
{
    std::cout << s << " " << "port" << std::endl;
}

int main(int args, char *argv[])
{
    if (args != 2)
    {
        usage(argv[0]);
        exit(1);
    }

    // 1.创建listen套接字
    int listenfd = Socket::Sock();

    // 2.绑定套接字
    int16_t port = (uint16_t)atoi(argv[1]);
    Socket::Bind(listenfd, port);

    // 3.监听套接字
    Socket::Listen(listenfd);

    // 4.监听完套接字之后，不能立即accept
    // accept本质上也是从listenfd这个文件描述符中获取连接
    // 所以必须得等listenfd这个文件描述符就绪之后，accept再去获取连接
    // 那么listenfd什么时候就绪呢，三次握手建立连接之后就就绪了
    // 所以此时就得先让poll帮忙等待listenfd就绪

    // 5.创建一个数组，保存要等待的文件描述符
    struct pollfd fd_arry[NUM];
    for (int i = 0; i < NUM; i++)
    {
        // 对fd_arry数据进行初始化
        fd_arry[i].fd = -1;
        fd_arry[i].events = 0;
        fd_arry[i].revents = 0;
    }

    // 6.将listenfd设置进fd_arry中，用第一个元素存储listen_fd
    fd_arry[0].fd = listenfd;
    fd_arry[0].events = POLLIN;
    fd_arry[0].revents = 0;

    for (;;)
    {
        // 7.调用poll，让poll等待文件描述符就绪。
        int n = poll(fd_arry, NUM, -1);

        switch (n)
        {
        case -1:
            std::cout << "poll error" << std::endl;
            break;
        case 0:
            std::cout << "等待超时" << std::endl;
            break;
        default:
            // 意味着poll等待成功
            for (int i = 0; i < NUM; i++)
            {
                // 判断fd_arry中要等待的文件描述符的读事件是否就绪
                if (fd_arry[i].revents & POLLIN)
                {
                    // struct poolfd 中的读事件就绪
                    std::cout << "sock: " << fd_arry[i].fd << " 上面有了读事件，可以读取了" << std::endl;
                    // fd_arry[i].fd读就绪
                    // 但是要判断这个fd_farry[i].fd是listenfd就绪还是普通fd就绪
                    if (fd_arry[i].fd == listenfd)
                    {
                        std::cout << "listen_sock: " << listenfd << " 有了新的链接到来" << std::endl;
                        // listenfd就绪了，就可以开始获取连接了
                        int newfd = Socket::Accept(listenfd);
                        //将连接读走之后，此时一定要设置listenfd的读不就绪，方便获取下一个连接
                       // fd_arry[i].revents=0;
                        if (newfd >= 0)
                        {
                            std::cout << "listen_sock: " << listenfd << " 获取新的链接成功" << std::endl;
                        }

                        // 获取了新连接的文件描述符，但是后续read要进行读取时，还得等待newfd就绪
                        // 所以就让poll去等待newfd就绪

                        // 先在fd_arry中找一个空元素
                        int pos = 1;
                        for (pos = 1; pos < NUM; pos++)
                        {
                            if (fd_arry[pos].fd == -1)
                            {
                                // 找到了空位置
                                break;
                            }
                        }
                        if (pos < NUM)
                        {
                            std::cout << "已经成功将新连接 " << newfd << " 加入了fd_arry中,表示其需要被poll等待" << std::endl;
                            fd_arry[pos].fd = newfd;
                            fd_arry[pos].events = POLLIN;
                            fd_arry[pos].revents = 0;


                        }
                        else
                        {
                            // pos非法
                            std::cout << "找到的pos位置非法,服务器满载了，无法获取当前连接" << std::endl;
                            close(newfd);
                        }
                    }
                    else
                    {
                        // fd_arry[i].fd是普通文件描述符就绪了，所以可以开始进行数据拷贝，即读取数据
                        char buffer[1024];
                        int s = read(fd_arry[i].fd, buffer, sizeof(buffer) - 1);
                        //fd_arry[i].revents=0;
                        if (s > 0)
                        {
                            buffer[s] = 0;
                            std::cout << "client: " << buffer << std::endl;
                            //将数据读走之后，要设置revents为0，表示文件描述符不就绪了
                        }
                        else if (s == 0)
                        {
                            // 对端关闭了连接
                            // 此时就要将当前fd_arry[i].fd从fd_arry数组中移除，因为对端关闭了连接
                            // 意味着再也不会向fd_arry[i].fd这个文件描述符的接收缓冲区中发送数据
                            // 也就意味着fd_arry[i].fd这个文件描述符的接收缓冲区永远为空
                            // 即再也不会就绪，所以等待fd_arry[i].fd就绪就是毫无意义的，所以不必再等待
                            close(fd_arry[i].fd);
                            fd_arry[i].fd = -1;
                            fd_arry[i].events = 0;
                            fd_arry[i].revents = 0;


                        }
                        else
                        {
                            // 读取出错
                            std::cout << " read error!" << std::endl;
                            close(fd_arry[i].fd);
                            fd_arry[i].fd = -1;
                            fd_arry[i].events = 0;
                            fd_arry[i].revents = 0;

                        }
                    }
                }
            }
            break;
        }
    }

    return 0;
}
